#include "FileQueue.h"
#include "DateTime.h"
#include "TextBuffer.h"
#include "Exception.h"

namespace nsfq{

CFileQueue::CFileQueue( const string& sPath, const string& sPrefix, const string& sPostfix) : m_sPath( sPath ), m_sPrefix( sPrefix ), m_sPostfix( sPostfix )
{
	init();
}

CFileQueue::~CFileQueue()
{
}

int CFileQueue::Start()
{
	try_restart();
	loop();
}

int CFileQueue::Start( const time_t startTime, const FileInterval fileInterval )
{
	m_startTime = startTime;
	m_currentTime = m_startTime;
	m_FileInterval = fileInterval;
	loop();
	return 0;
}

int CFileQueue::AfterOpenFile()
{
	return 0;
}

int CFileQueue::AfterCloseFile()
{
	return 0;
}

void CFileQueue::init()
{
	m_FileInterval = HOURLY;
	m_startTime = time(NULL);
	m_currentTime = m_startTime;
	m_fileSize = 0;
	m_bDebug = false;
}

void CFileQueue::loop()
{
	while(1)
	{
		string sCurrentFile;
		sCurrentFile = get_current_filename();

		if( is_first_time( sCurrentFile ) )
		{
			//try to cleanup last file
			string sLastFile = get_last_filename();
			handle_file( sLastFile );
			m_fileSize = 0;
		}

		handle_file( sCurrentFile );
		update_current();

		log();
		clean();

		sleep();
		m_statistics.loop++;
	}
}

int CFileQueue::handle_file( const string &sFileName )
{
	stFileStatus tFileStatus;
	tFileStatus.fd = -1;
	tFileStatus.modifiedTime = time(NULL);
	m_mapFileStatus[sFileName] = tFileStatus; 

	int fd = open( sFileName );
	if( fd < 0 )
	{
		fprintf(stderr, "file: %s, error: %s\n", sFileName.c_str(), strerror(errno));
		return -10;
	}

	struct stat stat;
	int ret = ::fstat( fd, &stat );
	if( 0 != ret )
	{
		fprintf( stderr, "file: %s, error: %s\n", sFileName.c_str(), strerror(errno) );	
		close(fd);
		return -20;
	}

	if( is_first_open(sFileName) )
		m_statistics.file++;

	off_t filesize = stat.st_size;

	tFileStatus.fd = fd;
	tFileStatus.stat = stat;
	m_mapFileStatus[sFileName] = tFileStatus; 

	if( is_debug_on() )
	{
		fprintf(stderr, "handling %s, lastsize: %d, currentsize: %d\n", sFileName.c_str(), int(m_fileSize), int(filesize) );
	}

	if( filesize != m_fileSize )
	{
		//more date is available
		int length = filesize - m_fileSize;
		char *p = new char[length];
		::lseek( fd, m_fileSize, SEEK_SET );
		int n = ::read( fd, p, length);

		CTextBuffer buffer( p, n );
		
		string sLine;
		while( ( n = buffer.GetLine( sLine ) ) > 0 )
		{
			m_statistics.line++;
			HandleLine( sLine );	
		}

		delete[] p;
		m_fileSize = filesize;
	}

	close( fd );
}

int CFileQueue::open( const string &sFileName )
{
	m_fd = ::open(sFileName.c_str(), O_RDONLY);
	return m_fd;
}

int CFileQueue::try_restart()
{
	string sLogFile = m_sPath + "/current.log";
	FILE *file = fopen( sLogFile.c_str(), "w");
	if( file == NULL )
	{
		fprintf( stderr, "open log file: %s", sLogFile.c_str() );
		return -1;
	}

	int pid, time, filesize;
	int ret = fscanf( file, "%d %d %d", &pid, &time, &filesize );
	if( 3 == ret )
	{
		m_currentTime = time;
		m_fileSize = filesize;
	}
	fclose(file);	
	return 0;
}

void CFileQueue::log()
{
	string sLogFile = m_sPath + "/current.log";
	FILE *file = fopen( sLogFile.c_str(), "w");
	if( file == NULL )
	{
		fprintf( stderr, "open log file: %s", sLogFile.c_str() );
	}

	fprintf(file, "%d %d %d\n", getpid(), int(m_currentTime), int(m_fileSize));
	fclose(file);	

	fprintf( stderr, "currentTime: %d, file: %d, line: %d, loop: %d\n", int(m_currentTime), m_statistics.file, m_statistics.line, m_statistics.loop);
}

void CFileQueue::clean()
{
	time_t now = time(NULL);
	if( m_mapFileStatus.size() > 50 )
	{
		map<string, stFileStatus>::iterator it;
		for( it = m_mapFileStatus.begin(); it != m_mapFileStatus.end(); )
		{
			if( now - it->second.modifiedTime > 60 * 60 )
			{
				m_mapFileStatus.erase(it++);
			}
			else
			{
				it++;
			}
		}
	}
}

int CFileQueue::sleep()
{
	::sleep(1);
}

int CFileQueue::update_current()
{
	time_t now = time(NULL);	
	int diff = get_single_interval();
	if( now - m_currentTime >= diff )
	{
		m_currentTime += diff - (m_currentTime % diff);
		return 1;
	}
	return 0;
}

int CFileQueue::get_single_interval()
{
	int iDiff = 0;
	switch( m_FileInterval  )
	{
		case DAILY:
			iDiff = 24 * 60 * 60;
			break;
		case HOURLY:
			iDiff = 60 * 60;
			break;
		case MINUTELY:
			iDiff = 60;
			break;
		default:
			throw CException( "not valid interval" );
	}
	return iDiff;
}

string CFileQueue::get_current_filename()
{
	return get_filename(m_currentTime);
}

string CFileQueue::get_last_filename()
{
	int diff = get_single_interval();
	time_t lastTime = m_currentTime - diff;
	return get_filename( lastTime );
}

string CFileQueue::get_filename(time_t time)
{
	CDateTime datetime( time );	
	string sTimeString;
	switch( m_FileInterval  )
	{
		case DAILY:
			sTimeString = datetime.ToDay();
			break;
		case HOURLY:
			sTimeString = datetime.ToHour();
			break;
		case MINUTELY:
			sTimeString = datetime.ToMinute();
			break;
		default:
			throw CException( "not valid interval" );
	}
	string sFileName = m_sPath;
	if( m_sPath[m_sPath.size() - 1] != '/' )
		sFileName += "/";
	sFileName += m_sPrefix + sTimeString + m_sPostfix;
	return sFileName;
}

};
